﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data.Common;
using System.Threading.Tasks;

namespace Dapper.Sharding.Maui
{
    public class DistributedTransaction : IDisposable, IAsyncDisposable
	{
		private IDatabase defaultDb = null;
		private (DbConnection, DbTransaction) defaultVal = default;
		private Dictionary<IDatabase, (DbConnection, DbTransaction)> dict = null;

		private static (DbConnection, DbTransaction) CreateConnAndTran(IDatabase db)
		{
			DbConnection conn = null;
			DbTransaction tran = null;
			try
			{
				conn = db.GetConn();
				tran = conn.BeginTransaction();
				return (conn, tran);
			}
			catch
			{
				tran?.Dispose();
				conn?.Dispose();
				throw;
			}
		}

		private static async Task<(DbConnection, DbTransaction)> CreateConnAndTranAsync(IDatabase db)
		{
			DbConnection conn = null;
			DbTransaction tran = null;
			try
			{
				conn = await db.GetConnAsync();
                tran = await conn.BeginTransactionAsync();
                return (conn, tran);
			}
			catch
			{
				if (tran != null)
				{
                    await tran.DisposeAsync();
                }
				if (conn != null)
				{
                    await conn.DisposeAsync();
                }
				throw;
			}

		}

		public (DbConnection, DbTransaction) GetVal(IDatabase db)
		{
			if (defaultDb == null) //第一次初始化
			{
				defaultVal = CreateConnAndTran(db);
				defaultDb = db;
				return defaultVal;
			}
			else if (defaultDb.Equals(db))
			{
				return defaultVal;
			}
			else
			{
				if (dict == null)//dict为null第一次创建
				{
					dict = new Dictionary<IDatabase, (DbConnection, DbTransaction)>();
					var val = CreateConnAndTran(db);
					dict.Add(db, val);
					return val;
				}
				else
				{
					var ok = dict.TryGetValue(db, out var val);
					if (!ok)
					{
						val = CreateConnAndTran(db);
						dict.Add(db, val);
					}
					return val;
				}
			}
		}

		public async Task<(DbConnection, DbTransaction)> GetValAsync(IDatabase db)
		{
			if (defaultDb == null) //第一次初始化
			{
				defaultVal = await CreateConnAndTranAsync(db);
				defaultDb = db;
				return defaultVal;
			}
			else if (defaultDb.Equals(db))
			{
				return defaultVal;
			}
			else
			{
				if (dict == null)//dict为null第一次创建
				{
					dict = new Dictionary<IDatabase, (DbConnection, DbTransaction)>();
					var val = await CreateConnAndTranAsync(db);
					dict.Add(db, val);
					return val;
				}
				else
				{
					var ok = dict.TryGetValue(db, out var val);
					if (!ok)
					{
						val = await CreateConnAndTranAsync(db);
						dict.Add(db, val);
					}
					return val;
				}
			}
		}

		public bool ShowResult { get; set; }

		public ConcurrentDictionary<IDatabase, bool> Result;

		public bool GetResult(IDatabase db)
		{
			if (!ShowResult)
			{
				throw new Exception("you must set ShowResult=true");
			}
			Result.TryGetValue(db, out var val);
			return val;
		}

		private void AddResult(IDatabase db, bool ok)
		{
			if (ShowResult)
			{
				if (Result == null)
				{
					Result = new ConcurrentDictionary<IDatabase, bool>();
				}
				Result.TryAdd(db, ok);
			}
		}

		public void Commit()
		{
			if (defaultDb != null)
			{
				try
				{
					defaultVal.Item2.Commit();
					AddResult(defaultDb, true);
				}
				catch
				{
					throw; //如果第一个提交失败，抛出异常执行Rollback全部回滚
				}
				defaultVal.Item2.Dispose();
				defaultVal.Item1.Dispose();
				defaultDb = null;
				defaultVal.Item2 = null;
				defaultVal.Item1 = null;
			}
			if (dict != null && dict.Count > 0)
			{
				foreach (var d in dict)
				{
					var item = d.Value;
					try
					{
						item.Item2.Commit();
						AddResult(d.Key, true);
					}
					catch
					{
						try
						{
							AddResult(d.Key, false);
							item.Item2.Rollback();
						}
						catch { }
					}
					finally
					{
						item.Item2.Dispose();
						item.Item1.Dispose();
					}
				}
				dict.Clear();
				dict = null;
			}
			done = true;
		}

		public async Task CommitAsync()
		{
			if (defaultDb != null)
			{
				try
				{
                    await defaultVal.Item2.CommitAsync();
                    AddResult(defaultDb, true);
				}
				catch
				{
					throw; //如果第一个提交失败，抛出异常执行Rollback全部回滚
				}
                await defaultVal.Item2.DisposeAsync();
                await defaultVal.Item1.DisposeAsync();
                defaultDb = null;
				defaultVal.Item2 = null;
				defaultVal.Item1 = null;
			}
			if (dict != null && dict.Count > 0)
			{
				foreach (var d in dict)
				{
					var item = d.Value;
					try
					{
                        await item.Item2.CommitAsync();
                        AddResult(d.Key, true);
					}
					catch
					{
						try
						{
							AddResult(d.Key, false);
                            await item.Item2.RollbackAsync();
                        }
						catch { }
					}
					finally
					{
                        await item.Item2.DisposeAsync();
                        await item.Item1.DisposeAsync();
                    }
				}
				dict.Clear();
				dict = null;
			}
			done = true;
		}

		public void Rollback()
		{
			if (defaultDb != null)
			{
				try
				{
					AddResult(defaultDb, false);
					defaultVal.Item2.Rollback();
				}
				finally
				{
					defaultVal.Item2.Dispose();
					defaultVal.Item1.Dispose();
				}
				defaultDb = null;
				defaultVal.Item2 = null;
				defaultVal.Item1 = null;
			}
			if (dict != null && dict.Count > 0)
			{
				foreach (var d in dict)
				{
					var item = d.Value;
					try
					{
						AddResult(d.Key, false);
						item.Item2.Rollback();
					}
					finally
					{
						item.Item2.Dispose();
						item.Item1.Dispose();
					}
				}
				dict.Clear();
				dict = null;
			}
			done = true;
		}

		public async Task RollbackAsync()
		{
			if (defaultDb != null)
			{
				try
				{
					AddResult(defaultDb, false);
                    await defaultVal.Item2.RollbackAsync();
                }
				finally
				{
                    await defaultVal.Item2.DisposeAsync();
                    await defaultVal.Item1.DisposeAsync();
                }
				defaultDb = null;
				defaultVal.Item2 = null;
				defaultVal.Item1 = null;
			}
			if (dict != null && dict.Count > 0)
			{
				foreach (var d in dict)
				{
					var item = d.Value;
					try
					{
						AddResult(d.Key, false);
                        await item.Item2.RollbackAsync();
                    }
					finally
					{
                        await item.Item2.DisposeAsync();
                        await item.Item1.DisposeAsync();
                    }
				}
				dict.Clear();
				dict = null;
			}
			done = true;
		}

		private bool done = false;

		#region Dispose

		private bool disposedValue;

		protected virtual void Dispose(bool disposing)
		{
			if (!disposedValue)
			{
				if (disposing)
				{
					if (!done)
					{
						Rollback();
					}
				}
				disposedValue = true;
			}
		}


		public void Dispose()
		{
			Dispose(disposing: true);
			GC.SuppressFinalize(this);
		}

        public async ValueTask DisposeAsync()
        {
            if (!done)
            {
                await RollbackAsync();
                done = true;
                disposedValue = true;
            }
            GC.SuppressFinalize(this);
        }

        #endregion
    }
}
